#include "stdafx.h"

#include "MvImageConvertHelper.h"
#include "GenICam/StreamSource.h"
#include "GenICam/PixelType.h"
#include "Infra/Guard.h"
#include <process.h>
#include "Media/ImageConvert.h"

#define RATE_CALC_TIME (3000)
#define DEFAULT_SHOW_RATE (30)

#define CHECK_IMGCNV_STATUS(vxOp) \
	do {\
			if (IMGCNV_SUCCESS != vxOp) \
			{ \
				return false; \
			} \
	} while (0)

namespace Dahua{

/**
* @enum gFormatTransferTbl
* @brief 图像像素格式 | Image pixel format
* @attention 无
*/
static uint32_t gFormatTransferTbl[] =
{	
	// Mono Format
	GenICam::gvspPixelMono1p,
	GenICam::gvspPixelMono8,
	GenICam::gvspPixelMono10,                
	GenICam::gvspPixelMono10Packed,			
	GenICam::gvspPixelMono12,			
	GenICam::gvspPixelMono12Packed,

	// Bayer Format		
	GenICam::gvspPixelBayRG8,                
	GenICam::gvspPixelBayGB8,                
	GenICam::gvspPixelBayBG8,                               
	GenICam::gvspPixelBayRG10,               
	GenICam::gvspPixelBayGB10,               
	GenICam::gvspPixelBayBG10,                             
	GenICam::gvspPixelBayRG12,               
	GenICam::gvspPixelBayGB12,          
	GenICam::gvspPixelBayBG12,                     
	GenICam::gvspPixelBayRG10Packed,			
	GenICam::gvspPixelBayGB10Packed,			
	GenICam::gvspPixelBayBG10Packed,			        
	GenICam::gvspPixelBayRG12Packed,			
	GenICam::gvspPixelBayGB12Packed,			
	GenICam::gvspPixelBayBG12Packed,			      
	GenICam::gvspPixelBayRG16,               
	GenICam::gvspPixelBayGB16,               
	GenICam::gvspPixelBayBG16,               

	GenICam::gvspPixelBayRG10p,              
	GenICam::gvspPixelBayRG12p,             

	GenICam::gvspPixelMono1c,

	// RGB Format
	GenICam::gvspPixelRGB8,				
	GenICam::gvspPixelBGR8,            

	// YVR Format
	GenICam::gvspPixelYUV411_8_UYYVYY,     
	GenICam::gvspPixelYUV422_8_UYVY,       
	GenICam::gvspPixelYUV422_8,         
	GenICam::gvspPixelYUV8_UYV, 
};
#define gFormatTransferTblLen	sizeof(gFormatTransferTbl)/sizeof(gFormatTransferTbl[0])

CMvImageConvertHelper::CMvImageConvertHelper(const GenICam::ICameraPtr& sptrCamera)
	: m_sptrCamera(sptrCamera)
	, m_bRunning(false)
//	, m_grabbingThread(Infra::CThreadLite::ThreadProc(&CMvImageConvertHelper::grabbingThreadProc, this), "GrabbingThread")
	, m_semQue(0)
	, m_iFrameRate(0)
	, m_iCalcTimeBegin(0)
	, m_iCalcTimeEnd(0)
	, m_iFrameCount(0)
	, m_dDisplayInterval(0)
	, m_nTimestampFreq(TIMESTAMPFREQUENCY)
	, m_nFirstFrameTime(0)
	, m_nLastFrameTime(0)
	, m_iControlNum(0)
    , m_hThread(NULL)
{
	setDisplayFPS(30);
	m_sptrStreamSrc = GenICam::CSystem::getInstance().createStreamSource(sptrCamera);
}

CMvImageConvertHelper::~CMvImageConvertHelper()
{
	stopConvert();
}

unsigned int CMvImageConvertHelper::threadProc(void* pParam)
{
	if ( !pParam )
	{
		return -1;
	}

	CMvImageConvertHelper* pThis = (CMvImageConvertHelper*)pParam;
	pThis->grabbingThreadProc(NULL);

	return 0;
}

bool CMvImageConvertHelper::isValid()
{
	return (!m_sptrStreamSrc) ? false : true;
}

bool CMvImageConvertHelper::startConvert(int iConvertRate)
{
	if ( !m_sptrStreamSrc )
	{
		return false;
	}

	if ( m_sptrStreamSrc->isGrabbing() )
	{
		return false;
	}

	bool bStartRet = m_sptrStreamSrc->startGrabbing();
	if ( !bStartRet )
	{
		return false;
	}

	m_bRunning = true;
	unsigned threadID = 0;
	m_hThread = (HANDLE)_beginthreadex( NULL, 0, &CMvImageConvertHelper::threadProc, (void*)this, 0, &threadID );

	//return m_grabbingThread.createThread();
	return true;
}

bool CMvImageConvertHelper::stopConvert()
{
    // 一直等待线程退出 直至线程退出
	// Wait until the thread exits
    destroyThread();

	if ( !m_sptrStreamSrc )
	{
		return false;
	}

	if ( !m_sptrStreamSrc->isGrabbing() )
	{
		return true;
	}	

	{
		Infra::CGuard guard(m_mutexQue);
		m_listImages.clear();
		m_semQue.post();
	}

	//m_grabbingThread.destroyThread();

	return m_sptrStreamSrc->stopGrabbing();
}

CMvImageBufPtr CMvImageConvertHelper::getConvertedImage()
{
	CMvImageBufPtr sptrConvertedImage;

	while ( m_bRunning )
	{
		m_mutexQue.enter();

		if ( m_listImages.empty() )
		{
			m_mutexQue.leave();
			m_semQue.pend();

			continue;
		}
		else
		{
			sptrConvertedImage = m_listImages.front();
			m_listImages.pop_front();

			m_mutexQue.leave();

			break;
		}		
	}
	
	return sptrConvertedImage;
}

void CMvImageConvertHelper::destroyThread()
{
    m_bRunning = false;

    if (m_hThread != NULL)
    {
        WaitForSingleObject(m_hThread, INFINITE);
        CloseHandle(m_hThread);
        m_hThread = NULL;
    }
}

void CMvImageConvertHelper::grabbingThreadProc(Infra::CThreadLite*)
{
	m_iCalcTimeBegin = ::GetTickCount();

	while ( m_bRunning )
	{
		if ( !m_sptrStreamSrc )
		{
			continue;
		}

		GenICam::CFrame frameBuf;
		m_sptrStreamSrc->getFrame(frameBuf, 100);
		if ( !frameBuf.valid() )
		{
			continue;
		}

		++m_iFrameCount;
		
		if ( 0 == m_iFrameRate )
		{
			m_iCalcTimeEnd = ::GetTickCount();
			if ( (m_iCalcTimeEnd - m_iCalcTimeBegin) > RATE_CALC_TIME )
			{
				m_iFrameRate = m_iFrameCount / (RATE_CALC_TIME / 1000);
				m_iControlNum = calcRateCtrlNum(m_iFrameRate);
			}
		}
		else
		{
			if ( (m_iFrameCount % m_iControlNum ) == 1 )
			{
				continue;
			}
		}

		if (!isTimeToDisplay())
		{
			continue;
		}

		int iFormatIndex = findMatchCode(frameBuf.getImagePixelFormat());
		if (iFormatIndex < 0)
		{
			printf("pixelFormat is not supported.\n");
			continue;;
		}
		else
		{
			if(!convertFrame(frameBuf))
			{
				printf("convertFrame is failed.\n");
				continue;
			}
		}	
	}

	m_iFrameRate = 0;
	m_iFrameCount = 0;
	m_iControlNum = 0;
}

bool CMvImageConvertHelper::isTimeToDisplay()
{
	// 不显示
	// don't display
	if (m_dDisplayInterval <= 0)
	{
		return false;
	}

	// 第一帧必须显示
	// The first frame must be displayed
	if (m_nFirstFrameTime == 0 || m_nLastFrameTime == 0)
	{
		m_nFirstFrameTime = Infra::CTime::getCurrentMicroSecond() * 1000;;
		m_nLastFrameTime = m_nFirstFrameTime;

		TRACE("set m_nFirstFrameTime: %I64d\n", m_nFirstFrameTime);
		return true;
	}

	// 当前帧和上一帧的间隔如果大于显示间隔就显示
	// Display if the interval between the current frame and the previous frame is greater than the display interval
	uint64_t nCurTimeTmp = Infra::CTime::getCurrentMicroSecond() * 1000;
	uint64_t nAcquisitionInterval = nCurTimeTmp - m_nLastFrameTime;
	if (nAcquisitionInterval > m_dDisplayInterval)
	{
		m_nLastFrameTime = nCurTimeTmp;
		return true;
	}

	// 当前帧相对于第一帧的时间间隔
	// Time interval between the current frame and the first frame
	uint64_t nPre = (m_nLastFrameTime - m_nFirstFrameTime) % m_dDisplayInterval;
	if (nPre + nAcquisitionInterval > m_dDisplayInterval)
	{
		m_nLastFrameTime = nCurTimeTmp;
		return true;
	}

	return false;
}

void CMvImageConvertHelper::setDisplayFPS(int nFPS)
{
	if (nFPS > 0)
	{
		Infra::CGuard guard(m_mxTime);
		m_dDisplayInterval = 1000 * 1000 * 1000.0 / nFPS;
	}
	else
	{
		Infra::CGuard guard(m_mxTime);
		m_dDisplayInterval = 0;
	}
}

bool CMvImageConvertHelper::convertFrame(const GenICam::CFrame& frameBuf)
{
	uint8_t* pSrcData = new(std::nothrow) uint8_t[frameBuf.getImageSize()];

	if(pSrcData)
	{
		memcpy(pSrcData, frameBuf.getImage(), frameBuf.getImageSize());
	}
	else
	{
		printf("m_pSrcData is null.\n");
		return false;
	}

	m_width = frameBuf.getImageWidth();
	m_height = frameBuf.getImageHeight();
	m_paddingX = frameBuf.getImagePadddingX();
	m_paddingY = frameBuf.getImagePadddingY();
	m_dataSize = frameBuf.getImageSize();
	m_pixelFormat = frameBuf.getImagePixelFormat();

	int bufferSize = 0;
	IMGCNV_HANDLE imgHandle = NULL;
	IMGCNV_SOpenParam openParam;
	openParam.width = m_width;
	openParam.height = m_height;
	openParam.paddingX = m_paddingX;
	openParam.paddingY = m_paddingY;
	openParam.dataSize = m_dataSize;
	openParam.pixelForamt = m_pixelFormat;

	bufferSize = m_width * m_height * 3;
	CMvImageBufPtr sptrImageBuf(new CMvImageBuf(bufferSize));

	IMGCNV_EErr status = IMGCNV_ConvertToBGR24(pSrcData, &openParam, sptrImageBuf->bufPtr(), &bufferSize);
	if (IMGCNV_SUCCESS != status)
	{
		delete[] pSrcData;
		printf("IMGCNV_open is failed.err=%d\n",status);
		return false;
	}

	addConvertedImage(sptrImageBuf);
	delete[] pSrcData;

	return true;
}

int CMvImageConvertHelper::findMatchCode(int iCode)
{
	for ( int i = 0; i < gFormatTransferTblLen; ++i )
	{
		if ( gFormatTransferTbl[i] == iCode )
		{
			return i;
		}
	}

	return -1;
}

uint32_t CMvImageConvertHelper::calcRateCtrlNum(uint32_t iFrameRate)
{
	uint32_t iControlNum = 1;

	if ( iFrameRate > DEFAULT_SHOW_RATE )
	{
		if ( iFrameRate < 40 )
		{
			iControlNum = 6;
		}
		else if ( iFrameRate < 50 )
		{
			iControlNum = 4;
		}
		else if ( iFrameRate < 60 )
		{
			iControlNum = 3;
		}
		else
		{
			iControlNum = 2;
		}
	}
	else
	{
		iControlNum = 1;
	}

	return iControlNum;
}

void CMvImageConvertHelper::addConvertedImage(const CMvImageBufPtr& sptrConvertedImage)
{
	static bool bThrowCtrl = true;

	Infra::CGuard guard(m_mutexQue);
	
	sptrConvertedImage->setImagePixelFormat(m_pixelFormat);
	sptrConvertedImage->setImageWidth(m_width);
	sptrConvertedImage->setImageHeight(m_height);

	if ( m_listImages.size() > DEFAULT_SHOW_RATE )
	{
		if ( bThrowCtrl )
		{
			m_listImages.pop_front();
			bThrowCtrl = false;
		}
		else
		{
			bThrowCtrl = true;
			return;
		}
	}

	m_listImages.push_back(sptrConvertedImage);
	m_semQue.post();
}
} // namespace Dahua
